using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;

namespace Project3
{
    /// <summary>
    /// This is the main type for your game
    /// </summary>
    public class Game1 : Microsoft.Xna.Framework.Game
    {
        private GraphicsDeviceManager graphics;
        private SpriteBatch spriteBatch;

        private SpriteFont font;

        private Texture2D projectile;
        private Texture2D gun;

        private Texture2D axis;
        private Texture2D cross;
        private Texture2D dot;

        private Vector2 offset;

        private Vector2 projectilePos;
        private Vector2 gunPos;
        private Vector2 targetPos;
        private Vector2 range;

        private List<Vector2> trail;

        private String title;
        private String x;
        private String y;
        private String other;

        private InputProcessor input;

        private bool isDrag = false;
        private bool isStop = true;

        private float time = 0;
        private float velocity;
        private const float g = 9.81f;
        private float correctAngle;
        private float currentAngle;

        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";
            graphics.PreferredBackBufferHeight = 800;
            graphics.PreferredBackBufferWidth = 800;
            IsMouseVisible = true;

            input = new InputProcessor(this);

            Components.Add(input);
        }

        /// <summary>
        /// Allows the game to perform any initialization it needs to before starting to run.
        /// This is where it can query for any required services and load any non-graphic
        /// related content.  Calling base.Initialize will enumerate through any components
        /// and initialize them as well.
        /// </summary>
        protected override void Initialize()
        {
            // TODO: Add your initialization logic here

            base.Initialize();
        }

        /// <summary>
        /// LoadContent will be called once per game and is the place to load
        /// all of your content.
        /// </summary>
        protected override void LoadContent()
        {
            // Create a new SpriteBatch, which can be used to draw textures.
            spriteBatch = new SpriteBatch(GraphicsDevice);

            // TODO: use this.Content to load your game content here
            font = Content.Load<SpriteFont>("font");

            projectile = Content.Load<Texture2D>("projectile");
            CreateRectangle(20, 10, out gun, Color.Blue);
            CreateRectangle(10, 3, out cross, Color.Red);
            CreateRectangle(800, 2, out axis, Color.Black);
            CreateRectangle(2, 2, out dot, Color.Red);

            offset = new Vector2(400, 400);

            projectilePos = new Vector2(-395, 0);
            gunPos = new Vector2(-395, 0);
            targetPos = new Vector2(395, 0);
            range = Vector2.Zero;

            currentAngle = 0.45f;

            velocity = 100;

            trail = new List<Vector2>();

            title = "Position\nProjectile\nTarget\nRange";
        }

        /// <summary>
        /// UnloadContent will be called once per game and is the place to unload
        /// all content.
        /// </summary>
        protected override void UnloadContent()
        {
            // TODO: Unload any non ContentManager content here
        }

        /// <summary>
        /// Allows the game to run logic such as updating the world,
        /// checking for collisions, gathering input, and playing audio.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Update(GameTime gameTime)
        {
            // Allows the game to exit
            if (Keyboard.GetState().IsKeyDown(Keys.Escape))
                this.Exit();

            // TODO: Add your update logic here
            if (input.isKeyPressed(Keys.Space)) isStop = !isStop;
            if (input.isKeyPressed(Keys.Back))
            {
                isStop = true;
                time = 0;

                trail.Clear();
            }

            if (isStop&&time==0)
            {
                if (input.isKeyPressed(Keys.W)) velocity += 10;
                if (input.isKeyPressed(Keys.S)) velocity -= 10;
                if (input.isKeyPressed(Keys.D)) targetPos.X += 10;
                if (input.isKeyPressed(Keys.A)) targetPos.X -= 10;
                velocity = MathHelper.Clamp(velocity, 100, 200);
                targetPos.X = MathHelper.Clamp(targetPos.X, 195, 395);

                if (input.isKeyPressed(Keys.Up)) currentAngle += 0.001f;
                if (input.isKeyPressed(Keys.Down)) currentAngle -= 0.001f;

                range.X = Math.Abs(gunPos.X) + Math.Abs(targetPos.X);

                correctAngle = (float)Math.Asin(9.81 * range.X / velocity / velocity) / 2;
            }

            if (!isStop)
            {
                time *= 1000;
                time += gameTime.ElapsedGameTime.Milliseconds;
                time /= 1000;

                trail.Add(projectilePos);
            }

            projectilePos.X = -395 + velocity * (float)Math.Cos(currentAngle) * time;
            projectilePos.Y = -(velocity * (float)Math.Sin(currentAngle) * time - g * time * time / 2);

            if (projectilePos.Y >= 0) isStop = true;

            x = "x(m)\n" + projectilePos.X + "\n" + targetPos.X + "\n" + range.X;
            y = "y(m)\n" + (-projectilePos.Y) + "\n" + (-targetPos.Y) + "\n" + (-range.Y);

            other = "Correct Gun angle:" + MathHelper.ToDegrees(correctAngle)
                + "\nCurrent Gun angle:" + MathHelper.ToDegrees(currentAngle)
                + "\nTime=" + time + "s\n"
                + "Velocity=" + velocity + "m/s\n";

            base.Update(gameTime);
        }

        /// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.CornflowerBlue);

            // TODO: Add your drawing code here
            spriteBatch.Begin();
            spriteBatch.Draw(axis, offset, null, Color.White, 0f, new Vector2(400, 1), 1f, SpriteEffects.None, 0f);
            spriteBatch.Draw(axis, offset, null, Color.White, MathHelper.PiOver2, new Vector2(400, 1), 1f, SpriteEffects.None, 0f);

            foreach (Vector2 dotPos in trail)
            {
                spriteBatch.Draw(dot, dotPos+offset, Color.White);
            }

            spriteBatch.Draw(gun, gunPos + offset, null, Color.White, -currentAngle, new Vector2(10, 5), 1f, SpriteEffects.None, 0f);

            spriteBatch.Draw(cross, targetPos + offset, null, Color.White, MathHelper.ToRadians(135), new Vector2(5, 1), 1f, SpriteEffects.None, 0f);
            spriteBatch.Draw(cross, targetPos + offset, null, Color.White, MathHelper.ToRadians(45), new Vector2(5, 1), 1f, SpriteEffects.None, 0f);

            spriteBatch.Draw(projectile, projectilePos + offset, null, Color.White, 0f, new Vector2(5, 5), 1f, SpriteEffects.None, 0f);

            spriteBatch.DrawString(font, title, new Vector2(420, 500), Color.White);
            spriteBatch.DrawString(font, x, new Vector2(550, 500), Color.White);
            spriteBatch.DrawString(font, y, new Vector2(700, 500), Color.White);

            spriteBatch.DrawString(font, other, new Vector2(10, 500), Color.White);

            spriteBatch.End();

            base.Draw(gameTime);
        }

        public void CreateRectangle(int width, int height, out Texture2D rectangleTexture, Color color)
        {
            // create the rectangle texture, ,but it will have no color! lets fix that
            rectangleTexture = new Texture2D(GraphicsDevice, width, height, false, SurfaceFormat.Color);

            // set the color to the amount of pixels
            Color[] colors = new Color[width * height];

            // loop through all the colors setting them to whatever values we want
            for (int i = 0; i < colors.Length; i++)
            {
                colors[i] = color;
            }

            // set the color data on the texture
            rectangleTexture.SetData(colors);

        }
    }
}
